/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;

/**
 * Bean post-processor that automatically applies asynchronous invocation
 * behavior to any bean that carries the {@link Async} annotation at class or
 * method-level by adding a corresponding {@link AsyncAnnotationAdvisor} to the
 * exposed proxy (either an existing AOP proxy or a newly generated proxy that
 * implements all of the target's interfaces).
 *
 * <p>The {@link TaskExecutor} responsible for the asynchronous execution may
 * be provided as well as the annotation type that indicates a method should be
 * invoked asynchronously. If no annotation type is specified, this post-
 * processor will detect both Spring's {@link Async @Async} annotation as well
 * as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation.
 *
 * <p>For methods having a {@code void} return type, any exception thrown
 * during the asynchronous method invocation cannot be accessed by the
 * caller. An {@link AsyncUncaughtExceptionHandler} can be specified to handle
 * these cases.
 *
 * <p>Note: The underlying async advisor applies before existing advisors by default,
 * in order to switch to async execution as early as possible in the invocation chain.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 3.0
 * @see Async
 * @see AsyncAnnotationAdvisor
 * @see #setBeforeExistingAdvisors
 * @see ScheduledAnnotationBeanPostProcessor
 */
/*
1. AsyncAnnotationBeanPostProcessor 总结
	异步方法处理器 - 自动为带有@Async注解的Bean应用异步调用行为的Bean后置处理器
	自动代理创建 - 通过添加AsyncAnnotationAdvisor到暴露的代理中实现异步执行
	灵活配置支持 - 支持自定义Executor和异常处理器，可配置不同的异步注解类型
2. 内部成员详解
	成员变量:
		DEFAULT_TASK_EXECUTOR_BEAN_NAME: String，默认任务执行器Bean名称"taskExecutor"
		logger: Log，日志记录器
		executor: Supplier<Executor>，异步执行器供应器
		exceptionHandler: Supplier<AsyncUncaughtExceptionHandler>，未捕获异常处理器供应器
		asyncAnnotationType: Class<? extends Annotation>，异步注解类型
	核心方法:
		configure() - 配置执行器和异常处理器供应器
		setExecutor() - 设置异步执行器
		setExceptionHandler() - 设置未捕获异常处理器
		setAsyncAnnotationType() - 设置异步注解类型
		setBeanFactory() - 设置Bean工厂并创建AsyncAnnotationAdvisor
3. 类关系结构
ProxyConfig (class)
└── ProxyProcessorSupport (class)
    └── AbstractAdvisingBeanPostProcessor (abstract class)
        └── AbstractBeanFactoryAwareAdvisingPostProcessor (abstract class)
              └── MethodValidationPostProcessor (class)
			  └── PersistenceExceptionTranslationPostProcessor (class)
		      └── AsyncAnnotationBeanPostProcessor (class)

相关类关系说明:
	AbstractBeanFactoryAwareAdvisingPostProcessor: 父类，提供BeanFactory感知的Advisor应用功能
	AsyncAnnotationAdvisor: 核心通知器，包含异步方法拦截逻辑
	Async: 默认的异步标记注解
	AnnotationAsyncExecutionInterceptor: 异步执行拦截器，实际处理异步调用
	TaskExecutor: Spring任务执行器接口
	AsyncUncaughtExceptionHandler: 异步未捕获异常处理器
	AsyncAnnotationBeanPostProcessor专门用于处理@Async注解，实现方法的异步执行。
4. 使用场景
	异步方法执行 - 将耗时方法标记为异步执行，提高应用响应性
	并发处理 - 在后台线程中处理非关键业务逻辑
	I/O密集型操作 - 异步处理文件操作、网络请求等I/O密集型任务
	邮件发送等耗时操作 - 在后台线程中发送邮件、生成报表等
	微服务调用 - 异步调用其他微服务以提高性能
5. 实际代码应用示例
=======案例1 ：基本配置
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-Executor-");
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}
======案例2 使用@Async注解
@Service
public class EmailService {


    // 带返回值的异步方法，或者不带返回值的异步方法
    @Async
    public Future<String> sendEmailWithResult(String to, String subject, String content) {
        try {
            Thread.sleep(3000);
            String result = "邮件发送成功: " + to;
            return new AsyncResult<>(result);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new AsyncResult<>("发送失败");
        }
    }

    // 使用自定义执行器
    @Async("customTaskExecutor")
    public void sendNotification(String message) {
        // 使用指定的执行器发送通知
        System.out.println("发送通知: " + message);
    }
}

// 自定义异常处理器
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        System.err.println("异步方法执行异常: " + method.getName());
        ex.printStackTrace();
    }
}



 */
@SuppressWarnings("serial")
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {

	/**
	 * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
	 * <p>Note that the initial lookup happens by type; this is just the fallback
	 * in case of multiple executor beans found in the context.
	 * @since 4.2
	 * @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME
	 */
	public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
			AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;


	protected final Log logger = LogFactory.getLog(getClass());

	@Nullable
	private Supplier<Executor> executor;

	@Nullable
	private Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;

	@Nullable
	private Class<? extends Annotation> asyncAnnotationType;



	public AsyncAnnotationBeanPostProcessor() {
		setBeforeExistingAdvisors(true);
	}


	/**
	 * Configure this post-processor with the given executor and exception handler suppliers,
	 * applying the corresponding default if a supplier is not resolvable.
	 * @since 5.1
	 */
	public void configure(
			@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

		this.executor = executor;
		this.exceptionHandler = exceptionHandler;
	}

	/**
	 * Set the {@link Executor} to use when invoking methods asynchronously.
	 * <p>If not specified, default executor resolution will apply: searching for a
	 * unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
	 * bean named "taskExecutor" otherwise. If neither of the two is resolvable,
	 * a local default executor will be created within the interceptor.
	 * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
	 * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
	 */
	public void setExecutor(Executor executor) {
		this.executor = SingletonSupplier.of(executor);
	}

	/**
	 * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
	 * exceptions thrown by asynchronous method executions.
	 * @since 4.1
	 */
	public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
		this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
	}

	/**
	 * Set the 'async' annotation type to be detected at either class or method
	 * level. By default, both the {@link Async} annotation and the EJB 3.1
	 * {@code javax.ejb.Asynchronous} annotation will be detected.
	 * <p>This setter property exists so that developers can provide their own
	 * (non-Spring-specific) annotation type to indicate that a method (or all
	 * methods of a given class) should be invoked asynchronously.
	 * @param asyncAnnotationType the desired annotation type
	 */
	public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
		Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
		this.asyncAnnotationType = asyncAnnotationType;
	}


	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);

		AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
		if (this.asyncAnnotationType != null) {
			advisor.setAsyncAnnotationType(this.asyncAnnotationType);
		}
		advisor.setBeanFactory(beanFactory);
		this.advisor = advisor;
	}

}
